package com.tuzhi.util;

import com.tuzhi.common.domain.JwtPayload;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import javax.crypto.SecretKey;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class JwtUtil {

    /*此外，JWT规定了7个可选官方字段（建议）

    属性	说明
    iss	JWT签发人
    exp	JWT过期时间
    sub	JWT面向用户
    aud	JWT接收方
    nbf	JWT生效时间
    iat	JWT签发时间
    jti	JWT编号


    iss (issuer)	签发人（谁签发的）
exp (expiration time)	过期时间，必须要大于签发时间
sub (subject)	主题（用来做什么）
aud (audience)	受众(给谁用的)比如：http://www.xxx.com
nbf (Not Before)	生效时间
iat (Issued At)	签发时间
jti (JWT ID)	编号，JWT 的唯一身份标识
    */

    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定JWT_SEC秘钥
     *
     * @param jwtSec    jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param ttlMillis jwt有效时长(毫秒)
     * @param userName  用户名 可根据需要传递的信息添加更多, 因为浏览器get传参url限制，不建议放置过多的参数
     * @return
     */
    public static String createJWT(String jwtSec, Long ttlMillis, String userName) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userName", userName);
        JwtPayload jwtPayload = new JwtPayload();
        Date now = new Date();
        jwtPayload.setIat(now);
        jwtPayload.setExp(DateUtils.addMilliseconds(now, ttlMillis.intValue()));
        return createJWT(jwtSec, claims, jwtPayload);
    }


    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定JWT_SEC秘钥
     *
     * @param jwtSec     jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param claims     创建payload的私有声明（根据特定的业务需要添加）
     * @param jwtPayload Jwt标准载荷
     * @return
     */
    public static String createJWT(String jwtSec, Map<String, Object> claims, JwtPayload jwtPayload) {
        // 指定签名的时候使用的签名算法，也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512;
        return createJWT(jwtSec, signatureAlgorithm, claims, jwtPayload);
    }

    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定JWT_SEC秘钥
     *
     * @param jwtSec     jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param jwtPayload Jwt标准载荷
     * @return
     */
    public static String createJWT(String jwtSec, JwtPayload jwtPayload) {
        // 指定签名的时候使用的签名算法，也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512;
        return createJWT(jwtSec, signatureAlgorithm, null, jwtPayload);
    }

    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定JWT_SEC秘钥
     *
     * @param jwtSec             jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param signatureAlgorithm 指定签名的时候使用的签名算法
     * @param claims             创建payload的私有声明（根据特定的业务需要添加）
     * @param jwtPayload         Jwt标准载荷
     * @return
     */
    public static String createJWT(String jwtSec, SignatureAlgorithm signatureAlgorithm, Map<String, Object> claims, JwtPayload jwtPayload) {
        SecretKey secretKey = generalKey(jwtSec);
        // 添加payload声明
        // 设置jwt的body
        JwtBuilder builder = Jwts.builder();
        if (claims != null) {
            builder.setClaims(claims);
        }
        if (StringUtils.isNotBlank(jwtPayload.getJti())) {
            builder.setId(jwtPayload.getJti());
        }
        if (StringUtils.isNotBlank(jwtPayload.getSub())) {
            builder.setSubject(jwtPayload.getSub());
        }
        if (StringUtils.isNotBlank(jwtPayload.getIssuer())) {
            builder.setIssuer(jwtPayload.getSub());
        }
        if (StringUtils.isNotBlank(jwtPayload.getAud())) {
            builder.setAudience(jwtPayload.getAud());
        }
        builder.setIssuedAt(jwtPayload.getIat());
        builder.setNotBefore(jwtPayload.getNbf());
        builder.setExpiration(jwtPayload.getExp());
        // 设置签名使用的签名算法和签名使用的秘钥
        builder.signWith(secretKey, signatureAlgorithm);
        return builder.compact();
    }


    /**
     * Token的解密
     *
     * @param jwtSec jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token  加密后的token
     * @return
     */
    public static Claims parseJWT(String jwtSec, String token) {
        SecretKey secretKey = generalKey(jwtSec);
        // 得到DefaultJwtParser
        Claims claims = Jwts.parserBuilder()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getEncoded())
                // 设置需要解析的jwt
                .build().parseClaimsJws(token).getBody();
        return claims;
    }


    /**
     * 根据token获取失效时间
     * 也是先从token中获取荷载
     * 然后从荷载中拿到到设置的失效时间
     *
     * @param token
     * @return
     */
    private static Date getExpiredDate(String jwtSec, String token) {
        Claims claims = parseJWT(jwtSec, token);
        return claims.getExpiration();
    }

    /**
     * 根据身份信息获取键值
     *
     * @param claims 身份信息
     * @param key    键
     * @return 值
     */
    public static String getValue(Claims claims, String key) {
        return claims.get(key) != null ? claims.get(key).toString() : null;
    }

    /**
     * 根据密码，生成密钥Key
     *
     * @param jwtSec
     * @return
     */
    public static SecretKey generalKey(String jwtSec) {
        //调用base64中的getDecoder方法获取解码器，调用解码器中的decode方法将明文密钥进行编码
        byte[] decodeKey = Base64.getDecoder().decode(jwtSec);
        SecretKey secretKey = Keys.hmacShaKeyFor(decodeKey);
        //返回加密后的密钥
        return secretKey;
    }

    public static void main(String[] args) {
        String jwtSec = "jwttuzhihygamwabcdefghijkltuzhi517465prodmnopqrstuvwxyztuzhihygamw20231223dxjwttuzhihygamw";
        // 生成jwt
//        String jwt = JwtUtil.createJWT(jwtSec, 10000L, "admin"); // 10秒过期
        Map<String, Object> claims1 = new HashMap<>();
        String uuid = UUID.randomUUID().toString();
        claims1.put("login_user_key", uuid);
        JwtPayload jwtPayload = new JwtPayload(uuid, "tuzhi", 10080, TimeUnit.MINUTES);
        String jwt = JwtUtil.createJWT(jwtSec, claims1, jwtPayload);
        System.out.println(jwt); // 生成token
        // 解析jwt
        Claims claims = parseJWT(jwtSec, jwt);
        // 获取用户名信息
        Object username = claims.get("userName");
        System.out.println("用户名:" + username); // 解析token

        // 签名算法
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512;
// HS256、HS384、HS512生成秘钥的方式
//        SecretKey secretKey = io.jsonwebtoken.security.Keys.secretKeyFor(signatureAlgorithm);
/*
        Date date=new Date(1704022422000L);
        System.out.println(DateFormatUtils.format(date,
                "yyyy-MM-dd HH:mm:ss"));
        System.out.println(new Date().getTime());
        System.out.println(System.currentTimeMillis());*/

    }
}